import cherrypy
import os
import pystache
import humanize
import json
import hashlib
import urllib.parse
from polyglot import aggregator
from polyglot.backends.support import ResourceCategory, ResourcePlain, ResourceRawHTML, ResourceLink, Quiz, Forum

import sys
backend = aggregator.load_backend(sys.argv[1])
config = {
    "Email": sys.argv[2],
    "Username": sys.argv[2],
    "Password": sys.argv[3],
    "School": sys.argv[4]
}

aggregator.authenticate(backend, config)

def render_grade_summary(s):
    return str(s * 100) + "%" if s is not None else "N/A"

cor = backend.courses
courses = [{"uuid": c.id, "name": c.title, "code": c.teacher, "term": render_grade_summary(c.grade_summary)} for c in cor]

task_list = []

for c in cor:
    try:
        task_list += [{"title": task.name, "class": c.title, "due": humanize.naturaltime(task.due_date), "cid": c.id} for task in c.tasks]
    except AttributeError:
        pass

def get_course(uuid):
    for c in cor:
        if c.id == uuid:
            return c

    return None

#TODO
def intl(text):
    return text

# TODO: CAREFUL!
def embed(text):
    if text.startswith("."):
        return "Nope"

    with open(text) as f:
        return f.read()

def svg_icon(text):
    if text[0] == ":":
        text = text[1:]

    if text.endswith("_link_icon"):
        text = text[0:-len("_link_icon")]

    # TODO: Don't be vulnerable FIXME
    with open("images/svg-icons/svg_icon_" + text + ".svg") as f:
        return f.read()

def template(templ, data):
    with open(templ, "r") as f:
        return pystache.render(f.read(), data)

def make_course_card(c):
    h = hashlib.md5(c["uuid"].encode("utf-8")).digest()
    color = "hsl(" + str(float(h[0])) + ", " + str(25 + (50*float(h[1])/256.0)) + "%, " + str(25 + (50*float(h[2])/256.0)) + "%)"

    return {
            "nickname": c["name"],
            "courseCode": c["code"],
            "originalName": c["name"],
            "term": c["term"],
            "href": "/courses?id=" + c["uuid"],
            "background": color,
            "backgroundColor": color,
            "links": [
                {"class": "announcements", "icon": embed("icons/announcement.mustache")},
                {"class": "assignments", "icon": embed("icons/assignment.mustache")},
                {"class": "discussions", "icon": embed("icons/discussion.mustache"), "unread": {"count": 0} },
            ]
    }

def make_dashboard(courses):
    return {
            "t": intl,
            "dashboard_courses": [make_course_card(c) for c in courses]
    }

def make_todo(assignments):
    return {
        "t": intl,
        "any_assignments": len(assignments) > 0,
        "assignments": [
            {**a, **({"classist": a["class"] is not None, "href": "/task?task=" + a["title"] + "&course=" + a["cid"]}) } for a in assignments
        ]
}

data_todo = make_todo(task_list)

def outer_template(content, data_todo, groups, expand=False):
    data = {
            "t": intl,
            "svg_icon": svg_icon,
            "embed": embed,
            "content": content,
            "groups": groups,
            "todo": data_todo,
            "expand": "ic-Layout-expand" if expand else ""
    }

    if len(groups) > 0:
        data["show-groups"] = True

    return template("views/layouts/application.mustache", data)

def inner_template(t, d, data_todo, groups, expand=False):
    return outer_template(lambda x: template(t, d), data_todo, groups, expand)

def serialise_post(post, base):
    return {
            "canReply": False,
            "user": post.author,
            "message": post.body,
            "date": "TODO",
            "reply-href": base + "&reply=" + post.id,
            "replies": [serialise_post(r, base) for r in post.replies]
    }

def dump_resource(rsrc, expand, base):
    out = ""

    if isinstance(rsrc, ResourceCategory):
        out += "<h1>" + rsrc.name + "</h1><ul>"

        for child in rsrc.children:
            out += "<li><a href='" + base + "/" + child.name + "'>" + child.name + "</a></li>"

        out += "</ul>"
    elif isinstance(rsrc, ResourceRawHTML):
        out += "<h1>" + rsrc.name + "</h1>" + rsrc.html
    elif isinstance(rsrc, ResourcePlain):
        out += "<h1>" + rsrc.name + "</h1>" + rsrc.text
    elif isinstance(rsrc, ResourceLink):
        out += "<a href='" + rsrc.url + "'>" + rsrc.name + "</a>" 
    elif not expand and (isinstance(rsrc, Quiz) or isinstance(rsrc, Forum)):
        out += "<a href='" + base + "/" + rsrc.name + "&full=1'>" + rsrc.name + "</a>" 
    elif expand and isinstance(rsrc, Quiz):
        out += "TODO: Quiz interface"
    elif expand and isinstance(rsrc, Forum):
        data = serialise_post(rsrc.thread, base)
        data["t"] = intl
        if hasattr(rsrc, "title"):
            data["title"] = rsrc.title

        out += template("views/discussion_topics/show.mustache", data)
    else:
        out += "<h1>" + rsrc.name + "</h1>Unknown element of type " + type(rsrc).__name__

    return out

resource_cache = {}

def path_serialise(path):
    return "/".join(map(str, path))

def find_resource(root, path, ref):
    if len(path) == 0:
        return root

    if len(ref) == 0:
        try:
            almost = resource_cache[path_serialise(path[:-1])]
            return find_resource(almost, [path[-1]], path[:-1])
        except KeyError:
            pass

    if isinstance(root, ResourceCategory):
        for child in root.children:
            if child.name == path[0]:
                rsrc = find_resource(child, path[1:], ref + [path[0]])
                resource_cache[path_serialise(ref + path)] = rsrc
                return rsrc

        print("Can't find " + path[0])
        return

    print("Wrong rsrc type with " + ",".join(path))

def find_task(lst, name):
    for task in lst:
        if task.name == name:
            return task

class PaletteServer(object):
    @cherrypy.expose
    def index(self):
        return self.dashboard()

    @cherrypy.expose
    def dashboard(self):
        return inner_template("views/users/user_dashboard.mustache", make_dashboard(courses), data_todo, [])

    @cherrypy.expose
    def grades(self):
        data_all_grades = {
                "t": intl,
                "courses": [{"name": c.title, "grade": render_grade_summary(c.grade_summary) } for c in cor]
        }

        return inner_template("views/users/grades.mustache", data_all_grades, data_todo, [])

    @cherrypy.expose
    def conversations(self, submit=False, compose=False, id="new", subject="", to="", course="", body=""):
        inbox = backend.inbox

        if submit:
            inbox.send(to, subject, body)

        if not compose:
            msgs = inbox.threads
            summaries = [{
                "read": "read",
                "date": thread[0].date,
                "participants": thread[0].sender + ", " + thread[0].recipient,
                "subject": thread[0].subject,
                "message_count": 0,
                "summary": "" # TODO: Fix
            } for thread in msgs]

            thread = msgs[0] # TODO: Selection
            replies = [{
                "avatar": "IMG",
                "author": msg.sender,
                "date": msg.date,
                "body": msg.body
            } for msg in thread] if thread is not None else []

            data = {
                    "t": intl,
                    "messages": summaries,
                    "subject": thread[0].subject if thread and thread[0] else "",
                    "replies": replies,
                    "id": id
            }

            return inner_template("views/conversations/index_new.mustache", data, [], [], expand=True)
        else:
            data = {
                    "t": intl,
                    "id": id 
            }

            return inner_template("views/jst/conversations/MessageFormDialog.mustache", data, [], [])

    @cherrypy.expose
    def task(self, course, task):
        t = find_task(get_course(course).tasks, task)
        o = "<h1>" + t.name + "</h1>" + "<p>" + humanize.naturaltime(t.due_date) + "</p>"
        rsrcs = t.resources
        if rsrcs is not None:
            for r in rsrcs:
                o += dump_resource(r, True, "/task?course=" + course + "&task=" + task)

        return outer_template(lambda _: o, data_todo, [])

    @cherrypy.expose
    def courses(self, id, active="home", data="", full=False, reply=None, do=None, args=""):
        course = get_course(id)

        # Filter TODO list for course
        course_todo = make_todo([t for t in task_list if t["cid"] == course.id])

        groups = [
            {"type": "home", "href": "/courses?id=" + id + "&active=home", "name": "Home", "show": True},
            {"type": "grades", "href": "/courses?id=" + id + "&active=grades", "name": "Grades", "show": hasattr(course, "grades")},
            {"type": "modules", "href": "/courses?id=" + id + "&active=modules", "name": "Modules", "show": hasattr(course, "resources")}
        ]

        for g in groups:
            if g["type"] == active:
                g["active"] = "active"

        if active == "grades":
            if course is None:
                print("Nope")
            report = course.grades

            lst = []
            for category in report:
                for grade in category.grades:
                    grade = {
                            "unread": False,
                            "title": grade.name,
                            "due": "N/A",
                            "score": grade.grade,
                            "possible": grade.possible, 
                            "category": category.name,
                            "comment": grade.comment
                    }

                    lst += [grade]

            data_grades = {
                    "t": intl,
                    "grades": lst,
                    "total": render_grade_summary(course.grade_summary)
            }

            return inner_template("views/gradebooks/grade_summary.mustache", data_grades, course_todo, groups)
        elif active == "modules":
            path = urllib.parse.unquote(data).split("/")
            rsrc = find_resource(c.resources, path[1:-1] if full else path[1:], [])

            if full or (do == "reply" and not hasattr(rsrc, "thread")):
                contents = rsrc.children
                for con in contents:
                    if con.name == path[-1]:
                        rsrc = con
            if do == "reply":
                thread = rsrc.thread
                rsrc.reply(rsrc.posts[reply], args)
            elif reply is not None:
                form = {
                        "t": intl,
                        "id": id,
                        "active": "modules",
                        "data": data,
                        "action": "/courses",
                        "reply": reply
                }

                return inner_template("views/jst/discussions/reply_form.mustache", form, course_todo, groups)

            return outer_template(lambda _: dump_resource(rsrc, True, "/courses?id=" + id + "&active=modules&data=" + data), course_todo, groups)
        elif active == "home":
            return outer_template(lambda _: "Hello!", course_todo, groups)
        else:
            return inner_template("views/error.mustache", {}, [], [])

conf = {
        '/': {
            'tools.staticdir.root': os.path.abspath(os.getcwd())
        },
        '/stylesheets': {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': './stylesheets'
        },
        '/icons': {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': './icons'
        }
}

cherrypy.quickstart(PaletteServer(), '/', conf)
